// $Id: output_header.cpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#include <iostream>
#include <boost/assert.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <xpgen/output_header.hpp>
#include <xpgen/model.hpp>
#include "names.hpp"

using namespace std;
namespace fs = boost::filesystem;

namespace xpgen {

output_header::output_header(output* prev) :
    output_cpp(prev),
    raw_interfaces(false),
    exact_includes(false)
{
}

output_header::~output_header()
{
}

string output_header::get_filename(interface_decl* itf) const
{
    return itf->name + "." + output_ext;
}

string output_header::get_type_info_type(interface_decl* itf) const
{
    string ret;
    ret += N_type_info;
    ret += "<";
    ret += get_relative_interface_qname_cpp(itf);
    ret += ">";
    return ret;
}

string output_header::get_interface_proxy_type(interface_decl* itf) const
{
    string ret;
    ret += N_interface_proxy;
    ret += "<";
    ret += get_relative_interface_qname_cpp(itf);
    ret += ", ";
    ret += N_TEMPL_Base;
    ret += ">";
    return ret;
}

void output_header::do_reset()
{
    output_cpp::do_reset();

    pstring_size_params_.clear();

    output_header* p = dynamic_cast<output_header*>(prev());
    if (p) {
        raw_interfaces = p->raw_interfaces;
        exact_includes = p->exact_includes;
    }
}

void output_header::do_prologue()
{
    output_cpp::do_prologue();

    if (verbose)
        cout << ">>> Writing interfaces..." << endl;

    // Check output directory
    fs::create_directories(output_dir);
}

void output_header::do_epilogue()
{
    if (verbose)
        cout << "<<< End writing interfaces." << endl;

    output_cpp::do_epilogue();
}

void output_header::do_interface(interface_decl* itf)
{
    BOOST_ASSERT(itf && itf->is_resolved() && itf->base);

    if (verbose)
        cout << "  interface: " << itf->context_name() << endl;

    // Open stream for interface
    string output_filename = get_filename(itf);
    fs::path output_file = output_dir / output_filename;

    fs::ofstream os(output_file);

    // Print file header informations
    print_file_header(os, output_filename);

    // Print inclusion guard begin
    string include_guard_name = get_include_guard_name(itf);

    os << endl;
    os << indent() << "#ifndef " << include_guard_name << endl;
    os << indent() << "#define " << include_guard_name << endl;

    // Print forward interfaces
    do_interface_reference(os, itf);

    // namespace begin
    if (!itf->ns_list.empty()) {
        os << endl;
        print_interface_ns_begin(os, itf);
    }

    // Print interface definition
    do_interface_def(os, itf);

    // namespace end
    if (!itf->ns_list.empty()) {
        os << endl;
        print_interface_ns_end(os, itf);
    }

    // Begin xpcom namespace
    os << endl;
    print_ns_begin(os, N_xpcom);

    // Print interface type_info
    do_interface_type_info(os, itf);

    if (!raw_interfaces) {
        // Begin xpcom::proxy namespace
        os << endl;
        print_ns_begin(os, N_proxy);

        // Print interface_proxy
        do_interface_proxy_def(os, itf);

        // End xpcom::proxy namespace
        os << endl;
        print_ns_end(os);
    }

    // End xpcom namespace
    os << endl;
    print_ns_end(os);

    // Print inclusion guard begin
    os << endl;
    os << indent() << "#endif // " << include_guard_name << endl;

    // Close stream
    os.close();
}

unsigned output_header::get_param_include_flags(param_decl* param) const
{
    unsigned ret = 0;

    // string is always here if has string param
    if (param->type.is_array) {
        ret = param->type.is_interface() ? inc_ptr_array : inc_array;
        ret |= inc_proxy_array;
    } else if (param->type.tag == type_decl::tag_bool) {
        ret = inc_proxy_bool;
    } else if (param->type.tag == type_decl::tag_uuid_ptr) {
        ret = inc_proxy_uuid_ptr;
    } else if (param->type.tag == type_decl::tag_utf8string) {
        ret = inc_ustring;
    } else if (param->type.is_string() || param->type.is_string_ptr()) {
        ret = inc_string;
    } else if (param->type.is_interface()) {
        ret = inc_ptr | inc_proxy_ptr;
    } else if (param->type.is_pstring()) {
        ret = inc_proxy_pstring;
        // if is output type, also need string
        if (!param->type.is_array && param->has_out() && !param->is_shared())
            ret |= inc_string;
    }

    return ret;
}

unsigned output_header::get_include_flags(interface_decl* itf) const
{
    if (raw_interfaces)
        return inc_interface;

    if (!exact_includes)
        return inc_xpcom;

    unsigned inc_flags = inc_interface | inc_ptr; // interface/ptr is needed always
    inc_flags |= inc_array_fwd; // for typedefs
    inc_flags |= inc_proxy_interface_proxy;

    BOOST_FOREACH(method_decl* method, itf->methods)
    {
        // If is regular method, then exception is needed
        if (!method->is_notxpcom())
            inc_flags |= inc_exception;

        inc_flags |= get_param_include_flags(method->result);

        BOOST_FOREACH(param_decl* param, method->params)
        {
            inc_flags |= get_param_include_flags(param);
        }
    }

    return inc_flags;
}

void output_header::do_interface_reference(ostream& os, interface_decl* itf)
{
    os << endl;

    // Includes
    print_includes(os, get_include_flags(itf));

    if (!itf->base->is_root()) {
        os << indent() << "#include \""
           << get_filename(itf->base) << "\"" << endl;
    }

    // Forward
    print_interface_forward(os, itf);
}

void output_header::do_interface_def(ostream& os, interface_decl* itf)
{
    os << endl;

    // uuid line
    //os << indent() << "// {" << itf->iid_str << "}" << endl;

    // class line
    os << indent() << "class " << N_XPCOM_NO_VTABLE << " ";
    if (write_scriptable && itf->is_scriptable()) {
        os << N_XPCOM_SCRIPTABLE << " ";
    }
    os << itf->name << " : public "
       << get_relative_interface_qname_cpp(itf->base) << endl;

    // class body
    os << indent() << "{" << endl;
    if (!itf->constants.empty() || !itf->methods.empty()) {
        os << indent() << "public:" << endl;
        push_indent();

        // constants
        if (!itf->constants.empty()) {
            os << indent() << "enum" << endl;
            os << indent() << "{" << endl;
            push_indent();

            for (size_t i = 0; i < itf->constants.size(); ++i) {
                do_constant(os, itf->constants[i]);
                if (i < itf->constants.size() - 1)
                    os << ",";
                os << endl;
            }

            pop_indent();
            os << indent() << "};" << endl;
        }

        // methods
        if (!itf->methods.empty()) {
            if (!itf->constants.empty())
                os << endl;

            BOOST_FOREACH(method_decl* method, itf->methods)
            {
                do_method(os, method);
            }

            // template qi methods
            BOOST_FOREACH(method_decl* method, itf->methods)
            {
                if (method->has_qi())
                    do_method_qi(os, method);
            }
        }

        // class body end
        pop_indent();
    }

    // class end
    os << indent() << "};" << endl;

    // typedefs
    if (!raw_interfaces) {
        os << endl;
        os << "#ifndef " << N_XPCOM_DISABLE_TYPEDEFS << endl;
        os << indent() << "typedef "
           << get_relative_xpcom_qname_cpp(N_ref_ptr)
           << "<" << itf->name << "> "
           << itf->name << N_SUFFIX_ptr << ";" << endl;
        os << indent() << "typedef "
           << get_relative_xpcom_qname_cpp(N_noref_ptr)
           << "<" << itf->name << "> "
           << itf->name << N_SUFFIX_nptr << ";" << endl;
        os << indent() << "typedef "
           << get_relative_xpcom_qname_cpp(N_ptr_array)
           << "<" << itf->name << "> "
           << itf->name << N_SUFFIX_array << ";" << endl;
        os << "#endif // " << N_XPCOM_DISABLE_TYPEDEFS << endl;
    }
}

void output_header::do_interface_type_info(ostream& os, interface_decl* itf)
{
    os << endl;

    // type_info declare
    os << indent() << N_XPCOM_TYPE_INFO_DECL << "("
       << get_relative_interface_qname_cpp(itf) << ", "
       << get_relative_interface_qname_cpp(itf->base) << ")"
       << endl;

    // type_info define
    os << endl;
    os << indent() << "// " << itf->iid_str << endl;
    os << indent() << N_XPCOM_TYPE_INFO_ID
       << "(" << get_relative_interface_qname_cpp(itf) << "," << endl;
    push_indent();
    os << indent() << itf->iid_value_str << ")" << endl;
    pop_indent();

    os << indent() << N_XPCOM_TYPE_INFO_NAME
       << "(" << get_relative_interface_qname_cpp(itf) << ", "
       << "\"" << itf->qname << "\")" << endl;
}

void output_header::do_interface_proxy_def(ostream& os, interface_decl* itf)
{
    os << endl;

    // class line
    os << "template <typename " << N_TEMPL_Base << ">" << endl;
    os << indent() << "class " << get_interface_proxy_type(itf) << " :"
       << endl;
    push_indent();
    os << indent() << "public " << get_interface_proxy_type(itf->base)
       << endl;
    pop_indent();

    // class body
    os << indent() << "{" << endl;
    if (!itf->methods.empty()) {
        os << indent() << "public:" << endl;
        push_indent();

        // methods
        for (size_t i = 0; i < itf->methods.size(); ++i) {
            if (i != 0)
                os << endl;

            method_decl* method = itf->methods[i];

            // XXX Now check and save the output pstring with size params
            // and also checks for mismatched [in out] flags
            pstring_size_params_.clear();
            BOOST_FOREACH(param_decl* param, method->params)
            {
                check_primary_param(param);
                check_sub_param(param);

                if (param->has_out() &&
                    is_param_pstring_with_size(param) &&
                    !is_param_out_isize2(param))
                {
                    pstring_size_params_.push_back(param);
                }
            }
            if (method->is_result_retval_param()) {
                param_decl* param = method->result;
                check_primary_param(param);
                check_sub_param(param);

                if (param->has_out() &&
                    is_param_pstring_with_size(param) &&
                    !is_param_out_isize2(param))
                {
                    pstring_size_params_.push_back(param);
                }
            }

            if (method->has_qi())
                do_proxy_method_qi(os, method);
            else
                do_proxy_method(os, method);
        }

        // class body end
        pop_indent();
    }

    // Print using _base;
    if (!itf->methods.empty())
        os << endl;
    os << indent() << "protected:" << endl;

    push_indent();
    os << indent() << "using "
       << get_interface_proxy_type(itf->base) << "::" << N_FUNC_base << ";"
       << endl;
    pop_indent();

    // class end
    os << indent() << "};" << endl;
}

void output_header::do_constant(ostream& os, constant_decl* constant)
{
//    os << indent() << N_XPCOM_STATIC_CONSTANT << "("
//       << get_type_name(&constant->type) << ", "
//       << get_constant_name_cpp(constant) << " = ";
    os << indent() << get_constant_name_cpp(constant) << " = ";

    switch (constant->type.tag) {
        case type_decl::tag_int16:
            os << constant->value.i16;
            break;
        case type_decl::tag_int32:
            os << constant->value.i32;
            break;
        case type_decl::tag_uint16:
            os << constant->value.ui16 << "U";
            break;
        case type_decl::tag_uint32:
            os << constant->value.ui32 << "U";
            break;

        default:
            BOOST_ASSERT(false && "invalid constant value type");
    }

//    os << ");" << endl;
}

void output_header::do_method(ostream& os, method_decl* method)
{
    os << indent();
    print_method_signature(os, method, raw_interfaces ? 0 : method_proxied);
    os << " = 0;" << endl;
}

void output_header::do_method_qi(ostream& os, method_decl* method)
{
    BOOST_ASSERT(method->qi_count > 0);

    os << endl;

    // Method template decl
    do_method_qi_templ_decl(os, method);

    // Method signature
    os << indent();
    do_method_qi_signature(os, method);
    os << endl;

    // Method body
    os << indent() << "{" << endl;
    push_indent();

    do_method_qi_body(os, method);

    pop_indent();
    os << indent() << "}" << endl;
}

void output_header::do_method_qi_templ_decl(ostream& os, method_decl* method)
{
    os << indent() << "template <";
    if (method->qi_count == 1) {
        os << "typename " << N_TEMPL_T << ">" << endl;
    } else {
        for (unsigned i = 0; i < method->qi_count; ++i) {
            if (i != 0)
                os << ", ";
            os << "typename "  << N_TEMPL_T << i + 1;
        }
        os << ">" << endl;
    }
}

void output_header::do_method_qi_signature(ostream& os, method_decl* method)
{
    if (method->is_notxpcom()) {
        os << N_XPCOM_METHOD_ << "("
           << get_param_type_name(method->result) << ") ";
    } else {
        os << N_XPCOM_METHOD << " ";
    }

    os << get_method_name_cpp(method, !raw_interfaces) << "(";

    size_t param_count = 0;
    for (size_t i = 0; i < method->params.size(); ++i) {
        param_decl* param = method->params[i];
        if (!param->is_iid_is()) {
            if (param_count++ != 0)
                os << ", ";
            os << get_param_type_name_qi(param);
            os << " " << param->name;
        }
    }

    // XXX result can not be iid_is
    if (method->is_result_retval_param()) {
        if (param_count > 0)
            os << ", ";
        os << get_param_type_name_qi(method->result);
        os << " " << method->result->name;
    }

    os << ")";
}

void output_header::do_method_qi_body(ostream& os, method_decl* method)
{
    os << indent();
    if (!method->is_result_void()) {
        os << "return ";
    }

    os << get_method_name_cpp(method, !raw_interfaces) << "(";

    for (size_t i = 0; i < method->params.size(); ++i) {
        if (i != 0)
            os << ", ";
        do_param_arg_qi(os, method->params[i]);
    }

    if (method->is_result_retval_param()) {
        if (!method->params.empty())
            os << ", ";
        do_param_arg_qi(os, method->result);
    }

    os << ");" << endl;
}

void output_header::do_proxy_method(ostream& os, method_decl* method)
{
    os << indent();
    do_proxy_method_signature(os, method);
    os << endl;

    os << indent() << "{" << endl;
    push_indent();

    do_proxy_method_body(os, method);

    pop_indent();
    os << indent() << "}" << endl;
}

void output_header::do_proxy_method_signature(ostream& os, method_decl* method)
{
    if (method->is_notxpcom()) {
        os << get_param_type_name(method->result);
    } else {
        os << get_proxy_param_type_name(method->result);
    }
    os << " ";

    os << get_proxy_method_name_cpp(method) << "(";

    size_t param_count = 0;
    for (size_t i = 0; i < method->params.size(); ++i) {
        param_decl* param = method->params[i];
        if (!is_proxy_param_sub(param)) {
            if (param_count++ != 0)
                os << ", ";
            os << get_proxy_param_type_name(param);
            os << " " << param->name;
        }
    }

    os << ")";
}

void output_header::do_proxy_method_body(ostream& os, method_decl* method)
{
    // Retval declare
    if (method->is_result_retval_param())
        do_proxy_retval_decl(os, method->result);

    // For pstring with size, we can not do them inplace
    do_proxy_pstring_size_decl(os);

    // Base call
    os << indent();
    if (method->is_notxpcom() && !method->result->type.is_void())
        os << "return ";

    os << N_FUNC_base << "()->"
       << get_method_name_cpp(method, !raw_interfaces) << "(";

    for (size_t i = 0; i < method->params.size(); ++i) {
        if (i != 0)
            os << ", ";
        do_proxy_param_arg(os, method->params[i]);
    }

    if (method->is_result_retval_param()) {
        if (!method->params.empty())
            os << ", ";
        do_proxy_param_arg(os, method->result);
    }

    os << ")";
    if (!method->is_notxpcom()) {
        os << " | " << N_FUNC_throw_exception;
    }
    os << ";" << endl;

    // Epilogue
    do_proxy_pstring_size_commit(os);

    if (method->is_result_retval_param())
        os << indent() << "return " << method->result->name << ";" << endl;
}

void output_header::do_proxy_retval_decl(ostream& os, param_decl* param)
{
    string init_str;

    type_decl* type = &param->type;
    if (type->is_array) {
        // Nothing
    } else if (type->is_pstring() && param->is_shared()) {
        init_str = "0";
    } else if (type->tag == type_decl::tag_bool) {
        init_str = N_false;
    } else if (type->tag == type_decl::tag_float) {
        init_str = "0.0F";
    } else if (type->tag == type_decl::tag_double) {
        init_str = "0.0";
    } else if (type->tag == type_decl::tag_native) {
        init_str = "0";
    } else if (type->tag == type_decl::tag_result ||
               type->tag == type_decl::tag_size ||
               type->is_standard())
    {
        init_str = "0";
    }

    os << indent() << get_proxy_param_type_name(param)
       << " " << param->name;

    if (!init_str.empty())
        os << " = " << init_str;
    os << ";" << endl;
}

void output_header::do_proxy_pstring_size_decl(ostream& os)
{
    BOOST_FOREACH(param_decl* param, pstring_size_params_)
    {
        os << indent();

        if (param->type.tag == type_decl::tag_pstring)
            os << N_pstr << "_";
        else if (param->type.tag == type_decl::tag_pwstring)
            os << N_pwstr << "_";
        else
            BOOST_ASSERT(false && "pstring_size: not pstring type");

        BOOST_ASSERT(param->has_out() && "pstring_size: not out/inout param");
        os << get_param_in_out_str(param);

        if (param->size_is == param->length_is)
            os << "_" << N_size2 << " ";
        else
            os << "_" << N_size << " ";

        os << param->name << "_("
           << param->name << ");" << endl;
    }
}

void output_header::do_proxy_pstring_size_commit(ostream& os)
{
#if 0
    BOOST_REVERSE_FOREACH(param_decl* param, pstring_size_params_)
    {
        os << indent() << param->name << "_." << N_FUNC_commit << "();" << endl;
    }
#endif
    for (size_t i = pstring_size_params_.size(); i > 0; --i) {
        os << indent()
           << pstring_size_params_[i - 1]->name << "_."
           << N_FUNC_commit << "();" << endl;
    }
}

void output_header::do_proxy_method_qi(ostream& os, method_decl* method)
{
    // Method template decl
    do_method_qi_templ_decl(os, method);

    // Method signature
    os << indent();
    do_proxy_method_qi_signature(os, method);
    os << endl;

    // Body
    os << indent() << "{" << endl;
    push_indent();

    do_proxy_method_qi_body(os, method);

    pop_indent();
    os << indent() << "}" << endl;

    // Need another format
    if (!method->is_notxpcom() && method->result->is_qi()) {
        os << endl;

        // Method template decl
        do_method_qi_templ_decl(os, method);

        // Method signature
        os << indent();
        do_proxy_method_qi_signature2(os, method);
        os << endl;

        // Body
        os << indent() << "{" << endl;
        push_indent();

        do_proxy_method_qi_body2(os, method);

        pop_indent();
        os << indent() << "}" << endl;
    }
}

void output_header::do_proxy_method_qi_signature(
        ostream& os, method_decl* method)
{
    if (method->is_notxpcom()) {
        os << get_param_type_name(method->result);
    } else {
        os << get_proxy_param_type_name_qi(method->result);
    }
    os << " ";

    os << get_proxy_method_name_cpp(method) << "(";

    size_t param_count = 0;
    for (size_t i = 0; i < method->params.size(); ++i) {
        param_decl* param = method->params[i];
        if (!is_proxy_param_sub(param)) {
            if (param_count++ != 0)
                os << ", ";
            os << get_proxy_param_type_name_qi(param);
            os << " " << param->name;
        }
    }

    os << ")";
}

void output_header::do_proxy_method_qi_signature2(
        ostream& os, method_decl* method)
{
    os << N_void << " ";

    os << get_proxy_method_name_cpp(method) << "(";

    size_t param_count = 0;
    for (size_t i = 0; i < method->params.size(); ++i) {
        param_decl* param = method->params[i];
        if (!is_proxy_param_sub(param)) {
            if (param_count++ != 0)
                os << ", ";
            os << get_proxy_param_type_name_qi(param);
            os << " " << param->name;
        }
    }

    BOOST_ASSERT(method->is_result_retval_param());
    if (param_count > 0)
        os << ", ";

    // TODO exception safety
    BOOST_ASSERT(method->result->is_retval());
    method->result->flags &= ~param_decl::flag_retval;
    os << get_proxy_param_type_name_qi(method->result);
    method->result->flags |= param_decl::flag_retval;
    os << " " << method->result->name;

    os << ")";
}

void output_header::do_proxy_method_qi_body(ostream& os, method_decl* method)
{
    // Has second format
    if (!method->is_notxpcom() && method->result->is_qi()) {
        os << indent() << get_proxy_param_type_name_qi(method->result)
           << " " << method->result->name << ";" << endl;

        os << indent() << get_proxy_method_name_cpp(method) << "(";

        size_t param_count = 0;
        for (size_t i = 0; i < method->params.size(); ++i) {
            param_decl* param = method->params[i];
            if (!is_proxy_param_sub(param)) {
                if (param_count++ != 0)
                    os << ", ";
                os << param->name;
            }
        }

        BOOST_ASSERT(method->is_result_retval_param());
        if (param_count > 0)
            os << ", ";
        os << method->result->name;

        os << ");" << endl;

        os << indent() << "return " << method->result->name << ";" << endl;

    } else {
        // Retval declare
        if (method->is_result_retval_param())
            do_proxy_retval_decl(os, method->result);

        // For pstring with size, we can not do them inplace
        do_proxy_pstring_size_decl(os);

        // Base call
        os << indent();
        if (method->is_notxpcom() && !method->result->type.is_void())
            os << "return ";

        os << N_FUNC_base << "()->"
           << get_method_name_cpp(method, !raw_interfaces) << "(";

        size_t param_count = 0;
        for (size_t i = 0; i < method->params.size(); ++i) {
            param_decl* param = method->params[i];
            if (!param->is_iid_is()) {
                if (param_count++ != 0)
                    os << ", ";
                do_proxy_param_arg_qi(os, method->params[i]);
            }
        }

        // XXX result can not be iid_is
        if (method->is_result_retval_param()) {
            if (param_count > 0)
                os << ", ";
            do_proxy_param_arg_qi(os, method->result); // XXX sure it is not qi
        }

        os << ")";
        if (!method->is_notxpcom()) {
            os << " | " << N_FUNC_throw_exception;
        }
        os << ";" << endl;

        // Epilogue
        do_proxy_pstring_size_commit(os);

        if (method->is_result_retval_param())
            os << indent() << "return " << method->result->name << ";" << endl;
    }
}

void output_header::do_proxy_method_qi_body2(ostream& os, method_decl* method)
{
    // XXX implies !method->is_notxpcom() && method->result->is_qi()
    // and no return value

    // For pstring with size, we can not do them inplace
    do_proxy_pstring_size_decl(os);

    // Base call
    os << indent() << N_FUNC_base << "()->"
       << get_method_name_cpp(method, !raw_interfaces) << "(";

    size_t param_count = 0;
    for (size_t i = 0; i < method->params.size(); ++i) {
        param_decl* param = method->params[i];
        if (!param->is_iid_is()) {
            if (param_count++ != 0)
                os << ", ";
            do_proxy_param_arg_qi(os, method->params[i]);
        }
    }

    // XXX Always has result as qi
    if (param_count > 0)
        os << ", ";
    do_proxy_param_arg_qi(os, method->result);

    os << ") | " << N_FUNC_throw_exception << ";" << endl;

    // Epilogue
    do_proxy_pstring_size_commit(os);
}

void output_header::do_param_arg(ostream& os, param_decl* param)
{
    BOOST_ASSERT(!param->type.is_void());
    os << param->name;
}

void output_header::do_param_arg_qi(ostream& os, param_decl* param)
{
    if (param->is_qi()) {
        if (param->has_out())
            os << "reinterpret_cast<" << N_void << "**>(";
        else
            os << "static_cast<" << N_void << "*>(";
        os << param->name << ")";

    } else if (param->is_iid_is()) {
        BOOST_ASSERT(param->method->qi_count > 0);

        os << get_relative_xpcom_qname_cpp(N_FUNC_typeid_of)
           << "<" << N_TEMPL_T;
        if (param->method->qi_count > 1)
            os << boost::lexical_cast<string>(param->primary()->qi_index + 1);
        os << ">()";

    } else {
        os << param->name;
    }
}

void output_header::do_proxy_param_arg(ostream& os, param_decl* param)
{
    // XXX Note: qi primary and sub is not considered here

    if (is_proxy_param_sub(param)) {
        param_decl* primary = param->primary();
        if (primary->type.is_array) {
            os << N_array << "_" << get_param_in_out_str(param) << "_"
               << get_param_size_length_str(param)
               << "(";
            for (unsigned i = 0; i < param->primary_count; ++i) {
                if (i > 0)
                    os << ", ";
                os << param->primary(i)->name;
            }
            os << ")";

        } else if (primary->type.is_pstring()) {
            os << primary->name << "_."
               << get_param_size_length_str(param) << "()";

        } else
            BOOST_ASSERT(false && "bad primary type of sub-param");

    } else if (is_proxy_param_primary(param)) {
        if (param->type.is_array) {
            if (is_param_out_isize2(param)) {
                os << N_array"_"N_out"_"N_isize2 << "(" << param->name
                   << ", " << param->size_is->name << ")";
            } else {
                os << N_array << "_" << get_param_in_out_str(param)
                   << "(" << param->name << ")";
            }

        } else if (param->type.is_pstring()) {
            if (is_param_out_isize2(param)) {
                if (param->type.tag == type_decl::tag_pstring)
                    os << N_pstr;
                else
                    os << N_pwstr;
                os << "_"N_out"_"N_isize2 << "(" << param->name
                   << ", " << param->size_is->name << ")";

            } else
                os << param->name << "_";

        } else
            BOOST_ASSERT(false && "bad primary type");

    } else if (param->type.tag == type_decl::tag_bool) {
        os << N_bool << "_" << get_param_in_out_str(param)
           << "(" << param->name << ")";

    } else if (param->type.tag == type_decl::tag_pstring) {
        os << N_pstr << "_" << get_param_in_out_str(param)
           << "(" << param->name << ")";

    } else if (param->type.tag == type_decl::tag_pwstring) {
        os << N_pwstr << "_" << get_param_in_out_str(param)
           << "(" << param->name << ")";

    } else if (param->type.tag == type_decl::tag_interface) {
        os << N_ptr << "_" << get_param_in_out_str(param)
           << "(" << param->name << ")";

    } else if (param->type.tag == type_decl::tag_interface_qi_out) {
        os << N_ptr << "_" << get_param_in_out_str(param) << "_" << N_void
           << "(" << param->name << ")";

    } else if (param->type.tag == type_decl::tag_uuid_ptr) {
        os << N_uuid_ptr << "_" << get_param_in_out_str(param)
           << "(" << param->name << ")";

    } else {
        if (param->has_out() && !param->type.is_dipper())
            os << "&";
        os << param->name;
    }
}

void output_header::do_proxy_param_arg_qi(ostream& os, param_decl* param)
{
    BOOST_ASSERT(!param->is_iid_is());

    if (param->is_qi()) {
        os << N_ptr << "_" << get_param_in_out_str(param)
           << "(" << param->name << ")";

    } else {
        do_proxy_param_arg(os, param);
    }
}

string output_header::get_param_type_name_qi(param_decl* param) const
{
    string ret;

    if (param->is_qi()) {
        method_decl* method = param->method;
        BOOST_ASSERT(method->qi_count > 0);

        if (method->qi_count == 1)
            ret = N_TEMPL_T"*";
        else
            ret = N_TEMPL_T +
                boost::lexical_cast<string>(param->qi_index + 1) + "*";
        if (param->has_out())
            ret += "*";

    } else {
        ret = get_param_type_name(param);
    }

    return ret;
}

string output_header::get_proxy_param_type_name_qi(param_decl* param) const
{
    string ret;

    if (param->is_qi()) {
        method_decl* method = param->method;
        BOOST_ASSERT(method->qi_count > 0);

        string basename;
        if (method->qi_count == 1)
            basename = N_TEMPL_T;
        else
            basename = N_TEMPL_T +
                boost::lexical_cast<string>(param->qi_index + 1);

        if (param->is_in())
            ret = get_relative_xpcom_qname_cpp(N_noref_ptr);
        else {
            ret = get_relative_xpcom_qname_cpp(N_ref_ptr);
        }
        ret += "<";
        ret += basename;
        ret += ">";

        if (!param->is_retval() && param->has_out()) {
            ret += "&";
        }

    } else {
        ret = get_proxy_param_type_name(param);
    }

    return ret;
}

} // namespace xpgen
